﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Asol.Reporting.Editor.Components;
using Asol.Reporting.Editor.ComponentsLayer;
using Asol.Reporting.Editor.Data;
using Asol.Reporting.Support;

namespace Asol.Reporting.Editor.Editor
{
	/// <summary>
	/// EditorControl : control, na kterém probíhá editace sestavy.
    /// Je postaven na vrstvové grafice (LayeredPanel), která umožňuje vykreslovat 
    /// statická data do jedné vrstvy a dynamická data do vrstvy nad ní, 
    /// urychlí se tak vykreslování při interaktivní editaci.
	/// </summary>
	public class EditorControl : LayeredPanelViewPort
	{
		#region Konstrukce a obsluha vnitřních eventů
        /// <summary>
        /// EditorControl : control, na kterém probíhá editace sestavy. Konstruktor.
        /// </summary>
		public EditorControl()
		{
            // this.SetStyle((ControlStyles.StandardClick | ControlStyles.StandardDoubleClick | ControlStyles.EnableNotifyMessage), true);
			this.SetStyle((ControlStyles.Selectable | ControlStyles.StandardDoubleClick | ControlStyles.UserMouse), true);
            this.LayerCount = 3;

            this.InitData();
            this.InitReportHeader();
            this.InitRulers();
            this.InitScrollbars();
			this.InitSplitters();
            this.InitMouse();
            this.KeyDown += new KeyEventHandler(EditorControl_KeyDown);
            this.SizeChanged += new EventHandler(ReportEditor_SizeChanged);
            this.ObjectState = EditorObjectState.Select;
			this.Initialized = true;
		}
        void EditorControl_KeyDown(object sender, KeyEventArgs e)
        {
            this.RunKeyboardAction(e);
        }
        void ReportEditor_SizeChanged(object sender, EventArgs e)
        {
            this.LayoutInnerDimensionsSet();
			this.ReDraw();
        }
		/// <summary>
		/// Příznak, že editor prošel plně inicializací
		/// </summary>
		protected bool Initialized = false;
		#endregion
        #region Inicializace
		/// <summary>
		/// Volá se po vytvoření Formu a umístění tohoto controlu na form, před vytvořením dokumentu.
		/// </summary>
        internal void Initialize()
        {
            // 31.10.2012 20:30:09  SourceAgent.exe config\43.config -a:HPT -r -e -v:43 -p:-1 -h:-1 -b -c -f -x  -t- -d:All  -comment:"Night build" -switch:cbModifyFiles:no:cbArchivPB:no:cbDataRunType:N   -g -t
            // 31.10.2012 19:00:03  SourceAgent.exe config\42.config -a:HPT -r -e -v:42 -p:-1 -h:-1 -b -c -f -x -t- -d:All -switch:cbDataRunType:N -comment:"Night build"   -g -t
            // 31.10.2012 15:30:00  SourceAgent.exe config\42.config -a:HPT -r -e -v:42 -p:-1 -h:-1 -b- -i- -x- -d- -t- -runad- -comment:"Test konzistence" -switch:cbModifyFiles:no:tbVersionRoot:\Inetpub\wwwroot\Check42 
        }
        #endregion
        #region Draw
        /// <summary>
        /// Kreslí kompletní data do grafického bufferu
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaintLayers(LayeredPaintEventArgs e)
        {
            base.OnPaintLayers(e);

			if (!this.Initialized) return;
		
            bool drawNext = false;                  // Po vykreslení konkrétní vrstvy nastavím na true, tím zajistím povinné vykreslení vyšší vrstvy.
            bool clearItems = true;                 // Před vykreslením konkrétní vrstvy je třeba odstranit položky této vrstvy a vrstev vyšších.
            if (e.HasPaintLayer(0))
            {   // Podkladová vrstva = Bands, Rulers, Scrollbars
                // Steward.Diagnostic.InfoAddLineWithTime("   EditorControl.OnPaintLayers(0)");

                Graphics graphics = e.GraphicsForLayer(0);
                if (clearItems)
                    this.ClearVisibleItems(0);
                this.PaintPrepareData(graphics);
                this.PaintReportHeader(graphics);
                this.PaintBandHeaders(graphics);
                this.PaintBandRows(graphics);
                this.PaintScrollbars(graphics);
                this.PaintRulers(graphics);
                this.PaintSplitters(graphics);
                drawNext = true;
                clearItems = false;
            }

            if (drawNext || e.HasPaintLayer(1))
            {
                // Steward.Diagnostic.InfoAddLineWithTime("   EditorControl.OnPaintLayers(1)");

                e.CopyContentOfLayer(0, 1);
                Graphics graphics = e.GraphicsForLayer(1);
                if (clearItems)
                    this.ClearVisibleItems(1);
                this.PaintComponents(graphics);
                drawNext = true;
                clearItems = false;
            }

            if (drawNext || e.HasPaintLayer(2))
            {
                // Steward.Diagnostic.InfoAddLineWithTime("   EditorControl.OnPaintLayers(2)");

                e.CopyContentOfLayer(1, 2);
                Graphics graphics = e.GraphicsForLayer(2);
                if (clearItems)
                    this.ClearVisibleItems(2);
                this.PaintComponentsInteractive(graphics);
                this.PaintFpsInfo(graphics);
                drawNext = true;
                clearItems = false;
            }
        }
        /// <summary>
        /// Připraví data pro kreslení.
        /// </summary>
        /// <param name="graphics">Grafika, do níž se má kreslit</param>
        protected void PaintPrepareData(Graphics graphics)
        {
            if (this.Document == null) return;

            // Steward.Diagnostic.InfoAddLineWithTime("    EditorControl.PaintPrepareData()");

            RectangleF areaF = this.ViewPort[EditorPart.BandHeader];
            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, EditMouseState.None);
            this.Document.PrepareDraw(args, 22f);
            this.EditorBandListClear();
            this.DocumentSizeToViewPort();
            this.ScrollbarsRefresh();
        }
        /// <summary>
        /// Do ViewPortu vloží logickou velikost dokumentu.
        /// Volá se typicky po přípravě dat v dokumentu (this.Document.PrepareDraw()), před kreslením scrolbarů (this.PaintScrollbars()).
        /// </summary>
        protected void DocumentSizeToViewPort()
        {
            SizeF docSize = this.Document.EditorDocumentSize;
            if (docSize == this.ViewPort.DocSize) return;

            this.ViewPort.DocSize = docSize;
            this.ScrollbarsRefresh();
        }
        /// <summary>
        /// Kreslí do grafického bufferu záhlaví svazků (Bands), všechny exitující (i IsContainers, IsCollapsed).
        /// Připravuje seznam svazků do editační plochy this.EditorBandList.
        /// </summary>
        /// <param name="graphics">Grafika, do níž se má kreslit</param>
        protected virtual void PaintBandHeaders(Graphics graphics)
        {
            if (this.Document == null) return;

            // Steward.Diagnostic.InfoAddLineWithTime("    EditorControl.PaintDrawBandHeaders()");

            RectangleF areaF = this.ViewPort[EditorPart.BandHeader];
            graphics.SetClip(areaF);
            graphics.FillRectangle(Brushes.LightGray, areaF);

            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, EditMouseState.None);
            IEnumerable<ReportBand> bands = this.Document.EditorBands;
            foreach (ReportBand band in bands)
            {
                args.Reset(band.CurrentMouseState);
                band.Paint(args);
                if (!band.IsContainer && !band.IsCollapsed)
                    this.EditorBandList.Add(band);
                this.AddVisibleItems(args, EditorLayer.Ground, band, InnerAreaType.BandHeaders);
            }
		}
        /// <summary>
        /// Vykreslí řádky za editované Bands (jsou v seznamu this.EditorBandList).
        /// </summary>
        /// <param name="graphics">Grafika, do níž se má kreslit</param>
        protected virtual void PaintBandRows(Graphics graphics)
        {
            if (this.Document == null) return;

            // Steward.Diagnostic.InfoAddLineWithTime("    EditorControl.PaintDrawBandRows()");

            RectangleF areaF = this.ViewPort[EditorPart.Document];
            Rectangle area = Rectangle.Round(areaF);
            graphics.FillRectangle(SystemBrushes.AppWorkspace, areaF);

            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, EditMouseState.None);
            foreach (ReportBand band in this.EditorBandList)
            {
                args.Reset(band.CurrentMouseState);
				band.DrawRow(this, args);
                this.AddVisibleItems(args, EditorLayer.Ground, band, InnerAreaType.BandRows);
            }
        }
        /// <summary>
        ///  Kreslí do grafického bufferu komponenty (Components)
        /// </summary>
        /// <param name="graphics">Grafika, do níž se má kreslit</param>
        protected virtual void PaintComponents(Graphics graphics)
        {
			if (this.Document == null) return;

            // Steward.Diagnostic.InfoAddLineWithTime("    EditorControl.PaintDrawComponents()");

            RectangleF areaF = this.ViewPort[EditorPart.Document];
            graphics.SetClip(areaF);

            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, EditMouseState.None);
			IEnumerable<ReportItem> items = this.Document.DrawComponentList;
            foreach (ReportItem item in items)
            {
                if (!item.IsOnInteractiveLayer)
                {
                    args.Reset(item.CurrentMouseState);
                    item.Paint(args);
                    this.AddVisibleItems(args, EditorLayer.Components, item, InnerAreaType.Components);
                }
            }

            this.SelectComponentWithFrameDraw(graphics);
        }
        /// <summary>
        ///  Kreslí do grafického bufferu komponenty (Components), které jsou v interaktivním stavu
        /// </summary>
        /// <param name="graphics">Grafika, do níž se má kreslit</param>
        protected virtual void PaintComponentsInteractive(Graphics graphics)
        {
            if (this.Document == null) return;

            // Steward.Diagnostic.InfoAddLineWithTime("    EditorControl.PaintDrawComponentsInteractive()");

            RectangleF areaF = this.ViewPort[EditorPart.Document];
            graphics.SetClip(areaF);

            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, EditMouseState.None);
            IEnumerable<ReportItem> items = this.Document.DrawComponentList;
            foreach (ReportItem item in items)
            {
                if (item.IsOnInteractiveLayer)
                {
                    args.Reset(item.CurrentMouseState);
                    item.Paint(args);
                    this.AddVisibleItems(args, EditorLayer.Interactive, item, InnerAreaType.Components);
                }
            }

            this.SelectComponentWithFrameDraw(graphics);
        }
        /// <summary>
        /// Vykreslí FPS
        /// </summary>
        /// <param name="graphics"></param>
        protected virtual void PaintFpsInfo(Graphics graphics)
        {
            double fps = Math.Round(this.TimeDrawFPS, 1);
            string text = fps.ToString("## ##0.0").Trim() + " fps";
            RectangleF hScrArea = this.ViewPort[EditorPart.HorizontalScrollBar];
            RectangleF area = new RectangleF(5, hScrArea.Top, hScrArea.Left - 7, hScrArea.Height);
            graphics.SetClip(area);
            UiRenderer.DrawString(graphics, text, SystemFonts.StatusFont, Brushes.Black, area, ContentAlignment.MiddleCenter);
        }
        #endregion
		#region Dokument (Data Object Model)
        /// <summary>
        /// Inicializace dat (Dokument)
        /// </summary>
        protected void InitData()
        {
			this._DocumentRef = null;
            this.ViewPort.ViewAreaId = EditorPart.Document;
            this.ViewPort.ViewArea = new RectangleF(new PointF(120f, 25f), new SizeF(1f, 1f));
            
            this.ViewPort.GetResolutionDPIFrom(this);
        }
        /// <summary>
		/// Data editovaného dokumentu.
		/// Ukládá si WeakReference na daný dokument.
		/// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ReportDocument Document
		{
			get
			{
				if (this.DocumentExists)
					return this._DocumentRef.Target as ReportDocument;
				return null;
			}
			set
			{
				if (value == null)
					this._DocumentRef = null;
				else
					this._DocumentRef = new WeakReference(value);
				this.ConnectDocumentAfter(value);
			}
		}
		/// <summary>
		/// příznak true, když je k dispozici objekt this.Document
		/// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool DocumentExists { get { return (_DocumentRef != null && _DocumentRef.IsAlive && _DocumentRef.Target is ReportDocument); } }
		/// <summary>
		/// WeakReference na dokument
		/// </summary>
		private WeakReference _DocumentRef;
		/// <summary>
		/// Zajistí aktualizaci svých vnitřních dat po vložení dat dokumentu
		/// </summary>
		/// <param name="document"></param>
		protected void ConnectDocumentAfter(ReportDocument document)
		{
            // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.ConnectDocumentAfter(%0)", (document == null ? "null" : "document"));
            if (document == null) return;
            this.VSplitter.SplitterPosition = document.EditorBandHeaderWidth;
            this.HSplitter.SplitterPosition = document.EditorHRulerHeight;
            this.LayoutInnerDimensionsSet();
            this.ViewPort.ResetView();
            this.Refresh();
		}
		#endregion
		#region Layout controlu: After Resize, refresh dimenzí
		/// <summary>
        /// Akceptuje vnější rozměry controlu, přizpůsobí jim vnitřní rozměry, ovlivní zobrazení Scrollbaru.
        /// Tato metoda nemá vyvolávat ReDraw(), a to ani zprostředkovaně (přes vyvolané eventy).
        /// </summary>
        protected void LayoutInnerDimensionsSet()
        {
            // Steward.Diagnostic.InfoAddLineWithTime(" EditorControl.LayoutInnerDimensionsSet()");

            float splt = this.SplitterThick;
            float splh = splt / 2f;
            int scrw = SystemInformation.VerticalScrollBarWidth;
            int scrh = SystemInformation.HorizontalScrollBarHeight;
            RectangleF clientArea = this.ClientArea;
            PointF componentBegin = new PointF(this.VSplitter.SplitterPosition + splh, this.HSplitter.SplitterPosition + splh);
            PointF componentEnd = new PointF(clientArea.Right - scrw - 1f, clientArea.Bottom - scrh - 1f);   // Bod vpravo dole, kde bude končit prostor pro komponenty. Potkávají se tam ScrollBary.
            SizeF componentSize = new SizeF(componentEnd.X - componentBegin.X, componentEnd.Y - componentBegin.Y);
            float reportHeaderWidth = componentBegin.X - clientArea.X - splt;
            float reportHeaderHeight = componentBegin.Y - splt - clientArea.Y;
            using (this.ViewPort.SuppressEvents(false))
            {
                // Určím velikost jednotlivých částí editoru, uložím to do ViewPortu:
                this.ViewPort[EditorPart.WholeClientArea] = clientArea;
                this.ViewPort[EditorPart.Document] = new RectangleF(componentBegin, componentSize);
                this.ViewPort[EditorPart.ReportHeader] = new RectangleF(clientArea.X, clientArea.Y, reportHeaderWidth + splt, reportHeaderHeight);
                this.ViewPort[EditorPart.BandHeader] = new RectangleF(clientArea.X, componentBegin.Y, reportHeaderWidth, componentSize.Height);
                this.ViewPort[EditorPart.HorizontalRule] = new RectangleF(componentBegin.X, clientArea.Y, componentSize.Width, reportHeaderHeight);
                this.ViewPort[EditorPart.BandDocumentSplitter] = new RectangleF(componentBegin.X - splt, componentBegin.Y, splt, componentSize.Height);
                this.ViewPort[EditorPart.RuleDocumentSplitter] = new RectangleF(clientArea.X, componentBegin.Y - splt, clientArea.Width, splt);
                this.ViewPort[EditorPart.VerticalScrollBar] = new RectangleF(componentEnd.X, componentBegin.Y, scrw, componentSize.Height);
                this.ViewPort[EditorPart.HorizontalScrollBar] = new RectangleF(componentBegin.X, componentEnd.Y, componentSize.Width, scrh);

                // Z ViewPortu přenesu souřadnice do těch komponent, které je potřebují znát:
                this.LayoutInnerDimensionsRefresh();
            }
        }
        /// <summary>
        ///  Z ViewPortu přenesu souřadnice do těch komponent, které je potřebují znát.
        ///  Přenese i parametry Scrollbarů. Potlačí event Change od Scrollbarů (Refresh nemá vyvolat Change).
        /// </summary>
        protected void LayoutInnerDimensionsRefresh()
        {
            // Steward.Diagnostic.InfoAddLineWithTime(" EditorControl.LayoutInnerDimensionsRefresh()");

            this.HRuler.CurrentVisualBounds = this.ViewPort[EditorPart.HorizontalRule];
            this.VSplitter.SplitterBounds = this.ViewPort[EditorPart.BandDocumentSplitter];
            this.HSplitter.SplitterBounds = this.ViewPort[EditorPart.RuleDocumentSplitter];
            this.VScr.Bounds = this.ViewPort[EditorPart.VerticalScrollBar];
            this.HScr.Bounds = this.ViewPort[EditorPart.HorizontalScrollBar];
            this.ScrollbarsRefresh();
        }
        #endregion
        #region Interaktivita - Práce s myší
        /// <summary>
        /// Inicializace proměnných myši
        /// </summary>
        protected void InitMouse()
        {
            this.CurrentAreaType = InnerAreaType.None;
            this.CurrentPressedButton = MouseButtons.None;
            this.MouseDownPoint = Point.Empty;
            this.MouseEnter += new EventHandler(EditorControl_MouseEnter);
            this.MouseMove += new MouseEventHandler(EditorControl_MouseMove);
            this.MouseDown += new MouseEventHandler(EditorControl_MouseDown);
            this.MouseUp += new MouseEventHandler(EditorControl_MouseUp);
            this.MouseWheel += new MouseEventHandler(EditorControl_MouseWheel);
            this.MouseLeave += new EventHandler(EditorControl_MouseLeave);
        }
        #region Primární eventy (EditorControl*) předávané z WinForm
        /// <summary>
        /// Myš vstoupila nad Control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseEnter(object sender, EventArgs e)
        {
            if (!this.Focused)
                this.Focus();
        }
        /// <summary>
        /// Myš se pohybuje bez stisknutí
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.None)
                this.ReportEditor_MouseMoveNone(sender, e);
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
                this.ReportEditor_MouseMoveLeft(sender, e);
        }
        /// <summary>
        /// Myš se stiskla
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseDown(object sender, MouseEventArgs e)
        {
            this.MouseDragFender = GetDragFender(e.Location);
            this.MouseDownPoint = e.Location;
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
                this.ReportEditor_MouseDownLeft(sender, e);
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)
                this.ReportEditor_MouseDownRight(sender, e);
        }
        /// <summary>
        /// Zvednutí myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.CurrentPressedButton == System.Windows.Forms.MouseButtons.Left)
                this.ReportEditor_MouseUpLeft(sender, e);
            else if (this.CurrentPressedButton == System.Windows.Forms.MouseButtons.Right)
                this.ReportEditor_MouseUpRight(sender, e);
        }
        /// <summary>
        /// Otočení kolečka myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseWheel(object sender, MouseEventArgs e)
        {
            // Kolečko myši reaguje následujícím způsobem:
            //  - bez ohledu na to, nad kterou částí editoru myš stojí
            //  - nehledí se na klávesy Shift ani Alt
            //  - pokud není stisknuta klávesa Ctrl, pak se provádí Scroll:
            //     - wheel nahoru => obsah jede dolů, blížíme se k Y souřadnici 0
            //     - wheel dolů => obsah jede nahoru, blížíme se k Y souřadnici Max
            //  - pokud je stisknuta klávesa Ctrl, pak se provádí Zoom
            //     - wheel nahoru => zvětšování
            //     - wheel nahoru => zmenšování
            bool needDraw = false;
            if (Control.ModifierKeys == Keys.Control)
                needDraw = this.ViewPort.WheelZoom(e.Location, e.Delta);
            else
                needDraw = this.ViewPort.WheelShiftY(e.Location, e.Delta);
            if (needDraw)
                this.ReDraw();
        }
        /// <summary>
        /// Myš opouští plochu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditorControl_MouseLeave(object sender, EventArgs e)
        {
            EditInteractiveEventArgs args = GetInteractiveArgs(Point.Empty, EditMouseChangeType.Leave);
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            this.CurrentAreaType = InnerAreaType.None;
            this.SetCursor(SysCursorType.Default);
            this.ProcessInteractiveArgsResult(args);
        }
        #endregion
        #region Logické eventy (ReportEditor*) roztříděné podle stavu buttonu myši, zde probíhá konverze fyzických událostí na události logické (například DragBegin, DragMove, LeftClick)
        /// <summary>
        /// Pohyb myši - bez tlačítka
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseMoveNone(object sender, MouseEventArgs e)
        {
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.None);
            InnerAreaType newAreaType = this.FindAreaType(e.Location);
            if (newAreaType != this.CurrentAreaType)
            {
                args.MouseChange = EditMouseChangeType.Leave;
                this.ReportEditor_MouseChange(this.CurrentAreaType, args);

                this.CurrentAreaType = newAreaType;
                args.MouseChange = EditMouseChangeType.Enter;
                this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            }
            args.MouseChange = EditMouseChangeType.Move;
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);

            this.ProcessInteractiveArgsResult(args);
        }
        /// <summary>
        /// Zmáčknutí levého tlačítka myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseDownLeft(object sender, MouseEventArgs e)
        {
            this.CurrentPressedButton = System.Windows.Forms.MouseButtons.Left;
            this.CurrentAreaType = this.FindAreaType(e.Location);
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.LeftDown);
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            this.ProcessInteractiveArgsResult(args);
        }
        /// <summary>
        /// Pohyb myši - se stisknutým levým tlačítkem (tažení)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseMoveLeft(object sender, MouseEventArgs e)
        {   // Zde nemá být detekce prostoru (this.CurrentAreaType), 
            //   protože tažení pomocí myši se týká již zmáčknutého prvku, 
            //   bez ohledu na to, zda se myš už nachází nad jiným prvkem.
            // Pokud je neprázdný Fender, a aktuální bod je uvnitř, pak Drag ještě nezačíná:
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.None);
            if (this.MouseDragFender.HasValue)
            {	// Je deklarován DragFender = ještě jsem nezačal Drag:
                if (this.MouseDragFender.Value.Contains(e.Location)) return;

                // Právě nyní zahajuji Drag:
                args.MouseChange = EditMouseChangeType.LeftDragBegin;
                this.ReportEditor_MouseChange(this.CurrentAreaType, args);
                this.MouseDragFender = null;               // Hodnota null bude v příštím běhu příznakem, že se už Fender nedetekuje (Drag už byl zahájen).
            }
            // Hned po LeftDragBegin se provede první LeftDragMove:
            args.MouseChange = EditMouseChangeType.LeftDragMove;
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            this.ProcessInteractiveArgsResult(args);
        }
        /// <summary>
        /// Zmáčknutí pravého tlačítka myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseDownRight(object sender, MouseEventArgs e)
        {
            this.CurrentPressedButton = System.Windows.Forms.MouseButtons.Right;
            this.CurrentAreaType = this.FindAreaType(e.Location);
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.RightDown);
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            this.ProcessInteractiveArgsResult(args);
        }
        /// <summary>
        /// Uvolnění (konec zmáčknutí) levého tlačítka myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseUpLeft(object sender, MouseEventArgs e)
        {
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.None);
            if (this.CurrentMouseChange == EditMouseChangeType.LeftDragMove)
            {   // Pokud už byl zahájen Drag, pak zde řešíme jeho konec:
                args.MouseChange = EditMouseChangeType.LeftDragEnd;
                this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            }
            else
            {   // Nebyl to Drag => jde o LeftUp = LeftClick
                args.MouseChange = EditMouseChangeType.LeftUp;
                this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            }
            args.MouseChange = EditMouseChangeType.None;
            this.ProcessInteractiveArgsResult(args);
            this.CurrentPressedButton = System.Windows.Forms.MouseButtons.None;
            this.MouseDownPoint = Point.Empty;
        }
        /// <summary>
        /// Uvolnění (konec zmáčknutí) pravého tlačítka myši
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReportEditor_MouseUpRight(object sender, MouseEventArgs e)
        {
            EditInteractiveEventArgs args = GetInteractiveArgs(e.Location, EditMouseChangeType.RightUp);
            this.ReportEditor_MouseChange(this.CurrentAreaType, args);
            this.ProcessInteractiveArgsResult(args);
            this.CurrentPressedButton = System.Windows.Forms.MouseButtons.None;
            this.MouseDownPoint = Point.Empty;
        }
        /// <summary>
        /// Najde a vrátí typ prostoru pro danou souřadnici myši.
        /// Typ prostoru: BandHeader, Components, Splitter, Scrollbar...
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        protected InnerAreaType FindAreaType(PointF point)
        {
            RectangleF vspl = this.ViewPort[EditorPart.BandDocumentSplitter];
            if (this.ViewPort[EditorPart.BandDocumentSplitter].Contains(point)) return InnerAreaType.VSplitter;
            if (this.ViewPort[EditorPart.RuleDocumentSplitter].Contains(point)) return InnerAreaType.HSplitter;
            if (this.ViewPort[EditorPart.ReportHeader].Contains(point)) return InnerAreaType.ReportHeader;
            if (this.ViewPort[EditorPart.VerticalScrollBar].Contains(point)) return InnerAreaType.VScr;
            if (this.ViewPort[EditorPart.HorizontalScrollBar].Contains(point)) return InnerAreaType.HScr;
            if (this.ViewPort[EditorPart.HorizontalRule].Contains(point)) return InnerAreaType.HRuler;
            if (this.ViewPort[EditorPart.BandHeader].Contains(point)) return InnerAreaType.BandHeaders;
            if (this.ViewPort[EditorPart.Document].Contains(point)) return InnerAreaType.Components;
            return InnerAreaType.None;
        }
        /// <summary>
        /// Obecná metoda: zjistí, zda konkrétní změna myši v dané oblasti 
        /// (pohyb, stisknutí / uvolnění tlačítka) má jako důsledek potřebu překreslení Controlu
        /// </summary>
        /// <param name="areaType"></param>
        /// <param name="args">Interaktivní data</param>
        /// <returns></returns>
        protected void ReportEditor_MouseChange(InnerAreaType areaType, EditInteractiveEventArgs args)
        {
            // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.MouseChange(AreaType=%0, Point=%1, MouseChange=%2)", this.CurrentAreaType, args.MousePointF, args.MouseChange);
            switch (this.CurrentAreaType)
            {
                case InnerAreaType.ReportHeader:
                    this.MouseChangeOnHeader(args);
                    break;
                case InnerAreaType.BandHeaders:
                    this.MouseChangeOnBand(args);
                    break;
                case InnerAreaType.Components:
                    this.MouseChangeOnComponent(args);
                    break;
                case InnerAreaType.VScr:
                    this.MouseChangeOnScrollbar(this.VScr, args);
                    break;
                case InnerAreaType.HScr:
                    this.MouseChangeOnScrollbar(this.HScr, args);
                    break;
                case InnerAreaType.HRuler:
                    this.MouseChangeOnHRuler(args);
                    break;
                case InnerAreaType.HSplitter:
                    this.MouseChangeOnSplitter(this.HSplitter, args);
                    break;
                case InnerAreaType.VSplitter:
                    this.MouseChangeOnSplitter(this.VSplitter, args);
                    break;
            }
        }
        /// <summary>
        /// Prostory s různým obsazením a různým chováním
        /// </summary>
        protected enum InnerAreaType
        {
            /// <summary>Prostor není obsazen</summary>
            None,
            /// <summary>Záhlaví reportu (tlačítko vlevo nahoře)</summary>
            ReportHeader,
            /// <summary>Záhlaví svazků (bands)</summary>
            BandHeaders,
            /// <summary>Prostor svazku pod komponentami</summary>
            BandRows,
            /// <summary>Data komponent</summary>
            Components,
            /// <summary>Vertikální scrollbar</summary>
            VScr,
            /// <summary>Horizontální scrollbar</summary>
            HScr,
            /// <summary>Horizontální pravítko</summary>
            HRuler,
            /// <summary>Horizontální oddělovač (pravítko a Components)</summary>
            HSplitter,
            /// <summary>Vertikální oddělovač (Záhlaví svazků a Components)</summary>
            VSplitter
        }
        #endregion
        #region Podpora: tvorba interaktivního argumentu, zpracování výsledků (redraw, change cursor), stavové proměnné
        /// <summary>
        /// Vrátí argument EditorInteractiveEventArgs pro daný bod a druh stavu, do argumentu přidá pointery na metody kalkulací
        /// </summary>
        /// <param name="mousePointF"></param>
        /// <param name="mouseChange"></param>
        /// <returns></returns>
        protected EditInteractiveEventArgs GetInteractiveArgs(PointF mousePointF, EditMouseChangeType mouseChange)
        {
            return new EditInteractiveEventArgs(this.ViewPort, this.ObjectState, mousePointF, this.MouseDownPoint, mouseChange);
        }
        /// <summary>
        /// Zpracuje výstupní požadavky uložené v args: NeedDraw a NeedCursor
        /// </summary>
        /// <param name="args"></param>
        protected void ProcessInteractiveArgsResult(EditInteractiveEventArgs args)
        {
            // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.ProcessInteractiveArgsResult(MouseChange=%0, NeedDraw=%1)", args.MouseChange, args.NeedDraw);
            this.CurrentMouseChange = args.MouseChange;
            this.CurrentMouseState = ConvertMouseChangeToState(args.MouseChange);
            if (args.NeedDraw)
                this.ReDraw(null, args.LayerToDraw);
            if (args.NeedCursor != SysCursorType.Null)
                this.SetCursor(args.NeedCursor);
        }
        /// <summary>
        /// Vrátí stav myši na základě aktuální změny stavu. Vrací stav po změně.
        /// </summary>
        /// <param name="mouseChange"></param>
        /// <returns></returns>
        public static EditMouseState ConvertMouseChangeToState(EditMouseChangeType mouseChange)
        {
            switch (mouseChange)
            {
                case EditMouseChangeType.None: return EditMouseState.None;
                case EditMouseChangeType.Enter: return EditMouseState.OnControl;
                case EditMouseChangeType.Move: return EditMouseState.OnControl;
                case EditMouseChangeType.LeftDown: return EditMouseState.MouseLeftDown;
                case EditMouseChangeType.LeftDragBegin: return EditMouseState.MouseDragMove;
                case EditMouseChangeType.LeftDragMove: return EditMouseState.MouseDragMove;
                case EditMouseChangeType.LeftDragEnd: return EditMouseState.OnControl;
                case EditMouseChangeType.LeftUp: return EditMouseState.OnControl;
                case EditMouseChangeType.RightDown: return EditMouseState.MouseRightDown;
                case EditMouseChangeType.RightUp: return EditMouseState.OnControl;
                case EditMouseChangeType.WheelUp: return EditMouseState.OnControl;
                case EditMouseChangeType.WheelDown: return EditMouseState.OnControl;
                case EditMouseChangeType.Leave: return EditMouseState.Outside;
            }
            return EditMouseState.None;
        }
        /// <summary>
        /// Nastaví požadovaný kurzor.
        /// Řeší paměť aktuálního stavu (this.CurrentCursorType), kurzor nastavuje pouze při změně.
        /// Žádná jiná metoda by neměla sahat na property this.Cursor.
        /// </summary>
        /// <param name="cursorType"></param>
        protected void SetCursor(SysCursorType cursorType)
        {
            if (this.CurrentCursorType != cursorType)
            {
                this.CurrentCursorType = cursorType;
                this.Cursor = SysCursors.GetCursor(cursorType);
            }
        }
        /// <summary>
        /// Vrátí souřadnice prostoru, v němž se bude ignorovat pohyb myši před zahájením Drag
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private Rectangle GetDragFender(Point point)
        {
            Size fender = SystemInformation.DragSize;
            return new Rectangle(new Point(point.X - fender.Width / 2, point.Y - fender.Height / 2), fender);
        }
        /// <summary>
        /// Aktuální prostor pod myší
        /// </summary>
        protected InnerAreaType CurrentAreaType;
        /// <summary>
        /// Posledně známý stav myši
        /// </summary>
        protected EditMouseState CurrentMouseState;
        /// <summary>
        /// Posledně známá aktivita myši
        /// </summary>
        protected EditMouseChangeType CurrentMouseChange;
        /// <summary>
        /// Aktuálně zmáčknuté tlačítko myši (v eventu MouseUp zde zjistíme, které bylo zmáčknuté před tím)
        /// </summary>
        protected MouseButtons CurrentPressedButton;
        /// <summary>
        /// Aktuálně používaný kurzor
        /// </summary>
        protected SysCursorType CurrentCursorType;
        /// <summary>
        /// Souřadnice, na které byla zmáčknuta myš.
        /// Koordináty: this Control, vizuální jednotky (pixel).
        /// </summary>
        protected Point MouseDownPoint;
        /// <summary>
        /// Rámeček okolo bodu, kde se zmáčkla myš.
        /// Koordináty: this Control, vizuální jednotky (pixel).
        /// Uvnitř tohoto rámečku se pohyb zmáčknuté myši ignoruje (tolerance chvění).
        /// Pokud se zmáčknutá myš bude pohybovat mimo tento rámeček, bude to prezentováno jak Tažení (Drag).
        /// </summary>
        protected Rectangle? MouseDragFender;
        #endregion
        #endregion
        #region ReportHeader
        /// <summary>
        /// Inicializuje data pro ReportHeader
        /// </summary>
        protected void InitReportHeader()
        {
            this.ReportHeader = new InteractiveButton();
            this.ReportHeader.Image = Asol.Reporting.Editor.Pics.IconLibrary32.Documentation;
            this.ReportHeader.ImageHot = Asol.Reporting.Editor.Pics.IconLibrary32.DocumentationHot;
            this.ReportHeader.ImagePressed = Asol.Reporting.Editor.Pics.IconLibrary32.DocumentationPressed;
        }
        /// <summary>
        /// Vykreslí ReportHeader
        /// </summary>
        /// <param name="graphics"></param>
        protected void PaintReportHeader(Graphics graphics)
        {
            RectangleF area = this.ViewPort[EditorPart.ReportHeader];
            graphics.SetClip(area);
            EditMouseState mouseState = (this.CurrentAreaType == InnerAreaType.ReportHeader ? this.CurrentMouseState : EditMouseState.Outside);
            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, area, mouseState);
            this.ReportHeader.Paint(args);
        }
        /// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti ReportHeader.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected void MouseChangeOnHeader(EditInteractiveEventArgs args)
        {
            this.ReportHeader.MouseChange(args);
        }
        /// <summary>
        /// Objekt představující tlačítko "Záhlaví reportu"
        /// </summary>
        protected InteractiveButton ReportHeader;
        #endregion
        #region Visible bands, components, segments
        /// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti BandsHeader.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
		protected void MouseChangeOnBand(EditInteractiveEventArgs args)
		{
			this.DetectChangeOnItem(args, false);
		}
        /// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti Components.
        /// Detekuje změnu prvku, který se nachází pod myší.
		/// Pokud se tam nachází stále totéž, vrací false (není třeba nic řešit).
		/// Pokud dojde ke změně, pak nový prvek (anebo null) vloží do this.ItemAtMouse
		/// </summary>
        /// <param name="args"></param>
		/// <returns></returns>
		protected void MouseChangeOnComponent(EditInteractiveEventArgs args)
		{
            if (this.IsInInsertObjectMode)
                this.DetectChangeOnInsertComponent(args);
            else
    			this.DetectChangeOnItem(args, true);
		}
		/// <summary>
		/// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti Bands nebo Components.
		/// Detekuje změnu prvku, který se nachází pod myší.
		/// Pokud se tam nachází stále totéž, vrací false (není třeba nic řešit).
		/// Pokud dojde ke změně, pak nový prvek (anebo null) vloží do this.ItemAtMouse
		/// </summary>
		/// <param name="args"></param>
		/// <param name="frameEnabled"></param>
		/// <returns></returns>
		protected void DetectChangeOnItem(EditInteractiveEventArgs args, bool frameEnabled)
		{
			bool requestRun = true;
            if (args.IsMouseChange(EditMouseChangeType.Move, EditMouseChangeType.Enter, EditMouseChangeType.Leave, EditMouseChangeType.LeftDown, EditMouseChangeType.RightDown))
                // Pokud již není myš stisknutá, detekuji aktuální prvek s myší, případně vyřeším jeho výměnu i ReDraw:
                requestRun = this.DetectChangeItemAtMouse(args);

            if (this.ItemAtMouse != null)
            {   // Akce do nalezeného elementu:
                if (requestRun)
                    this.ItemAtMouse.MouseChange(args);
            }
			else if (frameEnabled && args.IsMouseChange(EditMouseChangeType.LeftDown, EditMouseChangeType.LeftDragBegin, EditMouseChangeType.LeftDragMove, EditMouseChangeType.LeftDragEnd))
                // SelectWithFrame:
                this.SelectComponentWithFrame(args);
		}
        /// <summary>
        /// Zjistí, zda pod myší je nějaký prvek a jaký, zjistí zda se liší od toho dřívějšího, zajistí jeho aktualizaci včetně eventů do prvku při změně
        /// </summary>
        /// <param name="args"></param>
        protected bool DetectChangeItemAtMouse(EditInteractiveEventArgs args)
        {
            bool requestRun = true;
            
            EditorItem item = this.FindEditorItemAtPoint(args.MousePointF, itm => (itm.AreaType == InnerAreaType.BandHeaders || itm.AreaType == InnerAreaType.Components));
            // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.DetectChangeItemAtMouse(Point=%0, MouseChange=%1, Item=%2)", args.MousePointF, args.MouseChange, (item == null ? "null" : item.CurrentVisualArea.ToString()));
            if (!Object.ReferenceEquals(item, this.ItemAtMouse))
            {	// Změna prvku pod myší:
                EditMouseChangeType callerRequestChange = args.MouseChange;

                // Starý prvek opouštíme:
                if (this.ItemAtMouse != null)
                {
                    // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.DetectChangeItemAtMouse(Leave)");
                    args.MouseChange = EditMouseChangeType.Leave;
                    this.ItemAtMouse.MouseChange(args);
                    if (callerRequestChange == EditMouseChangeType.Leave)
                        requestRun = false;
                }

                // Na nový prvek vstupujeme:
                this.ItemAtMouse = item;
                if (this.ItemAtMouse != null)
                {
                    // Steward.Diagnostic.InfoAddLineWithTime("EditorControl.DetectChangeItemAtMouse(Enter)");
                    args.MouseChange = EditMouseChangeType.Enter;
                    this.ItemAtMouse.MouseChange(args);
                    if (callerRequestChange == EditMouseChangeType.Enter)
                        requestRun = false;
                }

                // Vrátím prvotní požadovanou akci:
                args.MouseChange = callerRequestChange;
            }
            return requestRun;
        }
        /// <summary>
        /// Z paměti viditelných prvků odstraní prvky dané vrstvy, a všech vrstev vyšších.
        /// Provádí se před novým vykreslováním dané vrstvy, které přidává do této paměti svoje nově vykreslené prvky na nových pozicích.
        /// </summary>
        /// <param name="layer"></param>
        protected void ClearVisibleItems(int layer)
        {
            if (layer <= 0 || this.VisibleItems == null)
                this.VisibleItems = new List<VisibleItem>();
            else
                this.VisibleItems = this.VisibleItems.Where(item => item.CurrentLayer < layer).ToList();
        }
        /// <summary>
        /// Do interního seznamu this.VisibleItems uloží aktivní prvky z dané položky.
        /// Akceptuje pouze souřadnice, které se nacházejí ve viditelné části (args.VisualArea).
        /// Částečně viditelné souřadnice ořízne.
		/// </summary>
		/// <param name="args"></param>
        /// <param name="layer">Číslo vrstvy</param>
		/// <param name="item">Reference na prvek</param>
        /// <param name="areaType">Druh prostoru</param>
        /// <returns></returns>
        protected void AddVisibleItems(EditPaintEventArgs args, int layer, EditorItem item, InnerAreaType areaType)
		{
			if (args.IsVisible && args.IsInteractive)
			{
				RectangleF editArea = args.VisualArea;
                foreach (SheetF itemSheet in args.ActiveVisualAreaList)
				{
                    itemSheet.Intersect(editArea);
					if (!itemSheet.IsEmpty)
                        this.VisibleItems.Add(new VisibleItem(itemSheet, layer, item, areaType));
				}
			}
		}
        /// <summary>
        /// Najde a vrátí prvek EditorItem, který se nachází pod myší.
        /// Pokud je jich na sobě v daném místě víc, vrátí ten, který je navrchu = je viditelný.
        /// Pokud není žádný, vrací null.
        /// </summary>
        /// <param name="point">Bod ve fyzických souřadnicích this Controlu = typicky MouseEventArgs.Location</param>
        /// <returns></returns>
        protected EditorItem FindEditorItemAtPoint(PointF point)
        {
            return this.FindEditorItemAtPoint(point, null);
        }
        /// <summary>
        /// Najde a vrátí prvek EditorItem, který se nachází pod myší.
        /// Pokud je jich na sobě v daném místě víc, vrátí ten, který je navrchu = je viditelný.
        /// Pokud není žádný, vrací null.
        /// </summary>
        /// <param name="point">Bod ve fyzických souřadnicích this Controlu = typicky MouseEventArgs.Location</param>
        /// <param name="predicate">Podmínka, která filtruje jednotlivé položky vyhovující dané souřadnici. Položky se procházejí od konce seznamu.</param>
        /// <returns></returns>
        protected EditorItem FindEditorItemAtPoint(PointF point, Func<VisibleItem, bool> predicate)
        {
            VisibleItem vItem = this.FindVisibleItemAtPoint(point, predicate);
            return (vItem == null ? (EditorItem)null : vItem.Item);
        }
        /// <summary>
        /// Najde a vrátí objekt VisibleItem (popisuje viditelný prvek), který se nachází pod myší.
		/// Pokud je jich na sobě v daném místě víc, vrátí ten, který je navrchu = je viditelný.
		/// Pokud není žádný, vrací null.
		/// </summary>
		/// <param name="point">Bod ve fyzických souřadnicích this Controlu = typicky MouseEventArgs.Location</param>
		/// <returns></returns>
        protected VisibleItem FindVisibleItemAtPoint(PointF point)
        {
            return this.FindVisibleItemAtPoint(point, null);
        }
        /// <summary>
        /// Najde a vrátí objekt VisibleItem (popisuje viditelný prvek), který se nachází pod myší.
		/// Pokud je jich na sobě v daném místě víc, vrátí ten, který je navrchu = je viditelný.
		/// Pokud není žádný, vrací null.
		/// </summary>
		/// <param name="point">Bod ve fyzických souřadnicích this Controlu = typicky MouseEventArgs.Location</param>
        /// <param name="predicate">Podmínka, která filtruje jednotlivé položky vyhovující dané souřadnici. Položky se procházejí od konce seznamu.</param>
        /// <returns></returns>
		protected VisibleItem FindVisibleItemAtPoint(PointF point, Func<VisibleItem, bool> predicate)
		{
            if (this.VisibleItems == null || this.VisibleItems.Count == 0) return null;

			for (int i = this.VisibleItems.Count - 1; i >= 0; i--)
			{
                VisibleItem item = this.VisibleItems[i];
                if (item.Sheet.Contains(point))
                {
                    if (predicate == null || predicate(item))
                        return item;
                }
			}
			return null;
		}
        /// <summary>
        /// Vyčistí a připraví seznam this.EditorBandList
        /// </summary>
        protected void EditorBandListClear()
        {
            if (this.EditorBandList == null)
                this.EditorBandList = new List<ReportBand>();
            else if (this.EditorBandList.Count > 0)
                this.EditorBandList.Clear();
        }
        /// <summary>
        /// Soupis svazků (Bands), které mohou být přítomny v editované ploše.
        /// Jde pouze o ty Bands, které jsou IsComponents a nejsou IsCollapsed
        /// </summary>
        protected List<ReportBand> EditorBandList;
        /// <summary>
        /// Souhrn všech viditelných prvků
        /// </summary>
		protected List<VisibleItem> VisibleItems;
		/// <summary>
		/// Prvek, který se nachází pod myší.
		/// Anebo null, když pod myší nic není.
		/// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected EditorItem ItemAtMouse { get; set; }
        /// <summary>
        /// Popis jednoho viditelného prvku (Band, komponenta): jeho souřadnice, jeho datový prvek.
        /// </summary>
		protected class VisibleItem
		{
            /// <summary>
            /// Popis jednoho viditelného prvku (Band, komponenta): jeho souřadnice, jeho datový prvek. Konstruktor.
            /// </summary>
            /// <param name="bounds"></param>
            /// <param name="currentLayer"></param>
            /// <param name="item"></param>
            /// <param name="areaType"></param>
            public VisibleItem(SheetF bounds, int currentLayer, EditorItem item, InnerAreaType areaType)
			{
				this.Sheet = bounds;
                this.CurrentLayer = currentLayer;
				this.Item = item;
                this.AreaType = areaType;
            }
            /// <summary>
            /// Viditelné souřadnice
            /// </summary>
			internal SheetF Sheet { get; private set; }
            /// <summary>
            /// Vrstva, ve které je prvek nyní vykreslen
            /// </summary>
            internal int CurrentLayer { get; private set; }
            /// <summary>
            /// Editační prvek
            /// </summary>
			internal EditorItem Item { get; private set; }
            /// <summary>
            /// Typ prostoru, kde se prvek nachází
            /// </summary>
            internal InnerAreaType AreaType { get; private set; }
		}
		#endregion
        #region Pravítka
        /// <summary>
        /// Inicializuje pravítka
        /// </summary>
        protected void InitRulers()
        {
            this.HRuler = new Ruler();
            this.HRuler.BackColor1 = Color.White;
            this.HRuler.BackColor2 = Color.LightGray;
            this.HRuler.Orientation = Orientation.Horizontal;
        }
        /// <summary>
        /// Vykreslí pravítka
        /// </summary>
        /// <param name="graphics">Grafika pro kreslení</param>
        protected void PaintRulers(Graphics graphics)
        {
            RectangleF areaF = this.ViewPort[EditorPart.HorizontalRule];
            graphics.SetClip(areaF);
            EditMouseState mouseState = (this.CurrentAreaType == InnerAreaType.HRuler? this.CurrentMouseState : EditMouseState.Outside);
            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, areaF, mouseState);
            this.HRuler.Paint(args);
        }
		/// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti HRuler.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
		protected void MouseChangeOnHRuler(EditInteractiveEventArgs args)
		{
			switch (args.MouseChange)
			{
				case EditMouseChangeType.LeftUp:
					this.Document.ReportGridSelect(args);
					break;
			}
		}
        /// <summary>
        /// Vodorovné pravítko, zobrazuje logické rozměry v aktuálním rozlišení a dané vizuální pozici
        /// </summary>
        protected Ruler HRuler;
        #endregion
        #region Scrollbary
        /// <summary>
        /// Inicializuje Scrollbary
        /// </summary>
        protected void InitScrollbars()
        {
            this.HScr = new Asol.Reporting.Editor.ComponentsLayer.ScrollBar();
            using (this.HScr.SuppressRecalculate())
            {
                this.HScr.Bounds = new RectangleF(35, 303, 450, 15);
                this.HScr.Minimum = 0;
                this.HScr.Maximum = 210;
                this.HScr.Showing = 50;
				this.HScr.ValueChanged += new EventHandler<ValueFloatChangedEventArgs>(HVScr_ValueChanged);
            }

            this.VScr = new Asol.Reporting.Editor.ComponentsLayer.ScrollBar();
            using (this.VScr.SuppressRecalculate())
            {
                this.VScr.Bounds = new RectangleF(485, 3, 15, 300);
                this.VScr.Minimum = 0;
                this.VScr.Maximum = 297;
                this.VScr.Showing = 50;
				this.VScr.ValueChanged += new EventHandler<ValueFloatChangedEventArgs>(HVScr_ValueChanged);
            }
        }
		/// <summary>
		/// Handler události po změně hodnoty na Scrollbarech (jak Horizontal, tak Vertical).
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void HVScr_ValueChanged(object sender, ValueFloatChangedEventArgs e)
		{
            this.ViewPort.DocAreaLocation = new PointF(this.HScr.CurrentBegin, this.VScr.CurrentBegin);
		}
        /// <summary>
        /// Do Scrollbarů přenese aktuální data z ViewPortu
        /// </summary>
        protected void ScrollbarsRefresh()
        {
            this.VScr.ScrollBarData = this.ViewPort.ScrollBarDataV;   // .Showing = this.EditorComponentArea.Height / this.ZoomRatio;
            this.HScr.ScrollBarData = this.ViewPort.ScrollBarDataH;   // .Showing = this.EditorComponentArea.Width / this.ZoomRatio;
        }
        /// <summary>
        /// Detekuje, zda pohybem myši nad daným scrollbarem došlo ke změně vizuálního stavu.
        /// </summary>
        /// <param name="scrollBar"></param>
        /// <param name="args">Interaktivní data</param>
        /// <returns></returns>
		private void MouseChangeOnScrollbar(Asol.Reporting.Editor.ComponentsLayer.ScrollBar scrollBar, EditInteractiveEventArgs args)
        {
            scrollBar.MouseChange(args);
        }
        /// <summary>
        /// Vykreslí Scrollbary
        /// </summary>
        /// <param name="graphics">Grafika pro kreslení</param>
        protected virtual void PaintScrollbars(Graphics graphics)
        {
            RectangleF area;
            EditPaintEventArgs args;
            EditMouseState mouseState;

            area = this.ViewPort[EditorPart.HorizontalScrollBar];
            graphics.SetClip(area);
            mouseState = (this.CurrentAreaType == InnerAreaType.HScr ? this.CurrentMouseState : EditMouseState.Outside);
            args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, area, mouseState);
            this.HScr.Paint(args);

            area = this.ViewPort[EditorPart.VerticalScrollBar];
            graphics.SetClip(area);
            mouseState = (this.CurrentAreaType == InnerAreaType.VScr ? this.CurrentMouseState : EditMouseState.Outside);
            args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, area, mouseState);
            this.VScr.Paint(args);
        }
        /// <summary>
        /// Vertikální scrollbar
        /// </summary>
        protected Asol.Reporting.Editor.ComponentsLayer.ScrollBar VScr;
        /// <summary>
        /// Horizontální scrollbar
        /// </summary>
        protected Asol.Reporting.Editor.ComponentsLayer.ScrollBar HScr;
        #endregion
		#region Splittery
        /// <summary>
        /// Šířka posuvných dělítek
        /// </summary>
        [Browsable(true)]
        [Category("Editor")]
        [Description("Šířka posuvných dělítek")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float SplitterThick
        {
            get { return _SplitterThick; }
            set
            {
                if (value > 0f && value <= 10f && value != this._SplitterThick)
                {
                    this._SplitterThick = value;
                    this.LayoutInnerDimensionsSet();
                    this.ReDraw();
                }
            }
        }
        private float _SplitterThick = 3f;
		/// <summary>
		/// Inicializuje splittery
		/// </summary>
		protected void InitSplitters()
		{
			this.VSplitter = new EditorSplitter(Orientation.Vertical);
			this.VSplitter.SplitterWeight = 3f;
            this.VSplitter.SplitterPositionMin = 60f;
            this.VSplitter.SplitterPositionMax = 600f;
			this.VSplitter.SplitterMoving += new EventHandler(VSplitter_SplitterMoving);

            this.HSplitter = new EditorSplitter(Orientation.Horizontal);
            this.HSplitter.SplitterWeight = 3f;
            this.HSplitter.SplitterPositionMin = 18f;
            this.HSplitter.SplitterPositionMax = 50f;
            this.HSplitter.SplitterMoving += new EventHandler(HSplitter_SplitterMoving);
        }
        /// <summary>
        /// Zpracuje změnu danou myší na Splitteru
        /// </summary>
        /// <param name="editorSplitter"></param>
        /// <param name="args"></param>
        protected void MouseChangeOnSplitter(EditorSplitter editorSplitter, EditInteractiveEventArgs args)
        {
            editorSplitter.MouseChange(args);
        }
		/// <summary>
		/// Handler události "Změna pozice VSplitteru"
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void VSplitter_SplitterMoving(object sender, EventArgs e)
		{
            this.Document.EditorBandHeaderWidth = this.VSplitter.SplitterPosition;
			this.LayoutInnerDimensionsSet();
            this.ReDraw();
		}
        /// <summary>
        /// Handler události "Změna pozice HSplitteru"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void HSplitter_SplitterMoving(object sender, EventArgs e)
        {
            this.Document.EditorHRulerHeight = this.HSplitter.SplitterPosition;
            this.LayoutInnerDimensionsSet();
            this.ReDraw();
        }
        /// <summary>
		/// Vykreslí splittery
		/// </summary>
        /// <param name="graphics">Grafika pro kreslení</param>
        protected void PaintSplitters(Graphics graphics)
		{
            EditPaintEventArgs args = new EditPaintEventArgs(this.ViewPort, this.ObjectState, graphics, this.ClientArea, EditMouseState.None);
            EditMouseState mouseState;

            graphics.ResetClip();

            mouseState = (this.CurrentAreaType == InnerAreaType.VSplitter ? this.CurrentMouseState : EditMouseState.Outside);
            args.Reset(mouseState);
            this.VSplitter.Paint(args);
            this.AddVisibleItems(args, EditorLayer.Ground, this.VSplitter, InnerAreaType.VSplitter);

            mouseState = (this.CurrentAreaType == InnerAreaType.HSplitter ? this.CurrentMouseState : EditMouseState.Outside);
            args.Reset(mouseState);
            this.HSplitter.Paint(args);
            this.AddVisibleItems(args, EditorLayer.Ground, this.HSplitter, InnerAreaType.HSplitter);
        }
		/// <summary>
        /// Splitter vertikální - mezi Bands a ViewArea
		/// </summary>
		protected EditorSplitter VSplitter;
        /// <summary>
        /// Splitter horizontální - mezi HRuler a ViewArea
        /// </summary>
        protected EditorSplitter HSplitter;
		#endregion
        #region Výběr komponent (selectování)
        /// <summary>
        /// Vykreslí Drag area, pokud je aktivní
        /// </summary>
        /// <param name="graphics">Grafika pro kreslení</param>
        protected void SelectComponentWithFrameDraw(Graphics graphics)
        {
            if (this.DragSelectArea.HasValue)
            {
                using (SolidBrush sb = new SolidBrush(Color.FromArgb(128, 226, 238, 252)))
                using (Pen pen = new Pen(Color.DarkBlue))
                {
                    pen.DashStyle = DashStyle.Custom;
                    pen.DashPattern = new float[] { 1, 2, 2, 2, 1, 1 };
                    graphics.FillRectangle(sb, this.DragSelectArea.Value);
                    graphics.DrawRectangle(pen, Rectangle.Round(this.DragSelectArea.Value));
                }
            }
        }
        /// <summary>
        /// Provádí výběr položek (komponenty) pomocí zarámování
        /// </summary>
        /// <param name="args"></param>
        protected void SelectComponentWithFrame(EditInteractiveEventArgs args)
        {
            bool isCtrl = (Control.ModifierKeys == Keys.Control);
            switch (args.MouseChange)
            {
                case EditMouseChangeType.LeftDown:
				case EditMouseChangeType.LeftDragBegin:
                    this.Document.SelectComponentFrameInit(!isCtrl);
                    if (this.DragSelectArea.HasValue)
                        // Začínám selectovat, a nějaký frame ještě svítí = zahodit:
                        this.DragSelectArea = null;
                    args.ReDrawLayer(EditorLayer.Components);
                    break;
                case EditMouseChangeType.LeftDragMove:
                    PointF point1 = this.MouseDownPoint;
                    PointF point2 = args.MousePointF;
                    this.DragSelectArea = point1.GetRectangleFromPoints(point2);
                    args.ReDrawLayer(EditorLayer.Components);
                    // RectangleF logicalArea = args.GetLogicalArea(this.DragSelectArea, CalculateAreaType.Component);
					this.Document.SelectComponentFrameAdd(this.DragSelectArea.Value);
                    break;
				case EditMouseChangeType.LeftDragEnd:
                case EditMouseChangeType.LeftUp:
                    this.Document.SelectComponentFrameDone();
                    this.DragSelectArea = null;
                    args.ReDrawLayer(EditorLayer.Components);
                    break;
            }
        }
        /// <summary>
        /// Prostor nad editorem komponent, který je zarámovaný pro Select.
        /// Pokud je Empty, pak není zarámované nic.
        /// </summary>
        protected RectangleF? DragSelectArea;
        #endregion
        #region Insertování komponent v editoru
        /// <summary>
        /// Metoda je vstupní metodou pro obsluhu všech akcí myši při insertování nové komponenty do reportu
        /// </summary>
        /// <param name="args"></param>
        protected void DetectChangeOnInsertComponent(EditInteractiveEventArgs args)
        {
            switch (args.MouseChange)
            {
                case EditMouseChangeType.Move:
                    this.InsertComponentMouseMove(args);
                    break;
                case EditMouseChangeType.LeftDown:
                    this.InsertComponentMouseDown(args);
                    break;
                case EditMouseChangeType.LeftUp:
                    this.InsertComponentMouseUp(args);
                    break;
                case EditMouseChangeType.LeftDragBegin:
                    this.InsertComponentDragBegin(args);
                    break;
                case EditMouseChangeType.LeftDragMove:
                    this.InsertComponentDragMove(args);
                    break;
                case EditMouseChangeType.LeftDragEnd:
                    this.InsertComponentDragEnd(args);
                    break;
            }
        }
        /// <summary>
        /// Reaguje na pohyb myši (bez stisknutého tlačítka) v prostoru komponent v době insertování komponenty
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentMouseMove(EditInteractiveEventArgs args)
        {
            EditorItem item = this.FindEditorItemAtPoint(args.MousePointF, itm => itm.AreaType == InnerAreaType.BandRows);
            if (item != null)
                args.NeedCursor = SysCursorType.ExtCrossDoc;
            else
                args.NeedCursor = SysCursorType.Default;
        }
        /// <summary>
        /// Vloží novou komponentu na aktuální bod myši, pokud se myš nachází na konkrétním BandRow
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentMouseDown(EditInteractiveEventArgs args)
        {
            ReportBand band = this.FindEditorItemAtPoint(args.MousePointF, itm => itm.AreaType == InnerAreaType.BandRows) as ReportBand;
            if (band == null) return;

            this.CurrentInsertComponent = this.Document.EditorInsertComponentCreate(band, this.CurrentObjectCommandKey, args.MousePointF);
            ReportComponent component = this.CurrentInsertComponent;
            if (component == null) return;

            // Určíme výchozí velikost s pomocí prvku reportu (může ovlivnit velikost, ale ne pozici):
            SizeF defaultLogicalSize = new SizeF(25.4f, 3.175f);
            SizeF defaultVisualSize = args.ViewPortConvertor.VisualFromLogical(defaultLogicalSize);
            RectangleF visualBounds = new RectangleF(args.MousePointF, defaultVisualSize);
            RectangleF logicalBounds = component.GetLogicalBoundsFromEditor(visualBounds);

            ReportObjectLogicalBoundsChangeArgs subArgs = new ReportObjectLogicalBoundsChangeArgs(args, component, LogicalBoundsChangeType.Insert, logicalBounds);
            component.ReportObject.EditorLogicalBoundsChange(subArgs);    // Požádáme prvek, aby nám vytvořil svoji výchozí velikost
            RectangleF? resultLogicalBounds = subArgs.LogicalBounds;
            
            if (resultLogicalBounds.HasValue && !resultLogicalBounds.Value.Size.IsEmpty)
            {   // Prvek nastavil neprázdnou logickou velikost : převezmeme pouze velikost:
                visualBounds.Size = args.ViewPortConvertor.VisualFromLogical(resultLogicalBounds.Value.Size);
            }
            else
            {   // Prvek nenastavil velikost, anebo ji dokonce sám zrušil : přidělíme velikost výchozí, Width = 25.4, Height = (25.4 / 8) :
                visualBounds.Size = args.ViewPortConvertor.VisualFromLogical(defaultLogicalSize);
            }

            component.SetVisualBound(visualBounds);               // Komponentu umístíme na dané vizuální souřadnice, včetně jejího zařazení do Band
            component.MouseChange(args);                          // Objekt detekuje stav myši LeftDown, na jehož základě se stane příslušným do vrstvy Interactive

            // Provedu ReDraw jen interaktivní vrstvy:
            args.ReDrawLayer(EditorLayer.Interactive);            // Objekt si určí svoje interaktivní segmenty a vykreslí se s nimi
        }
        /// <summary>
        /// Myš byla stisknuta a uvolněna na témže místě (bez MouseDrag): dokončíme insert objektu
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentMouseUp(EditInteractiveEventArgs args)
        {
            this.InsertComponentDone(args);
        }
        /// <summary>
        /// Vkládání komponenty, začátek Drag: začínáme provádět resize
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentDragBegin(EditInteractiveEventArgs args)
        {
            if (this.CurrentInsertComponent == null) return;

            // V komponentě aktivuji segment BottomRight, nebo MiddleRight, za ně budeme komponentu resizovat:
            bool isActivated = this.CurrentInsertComponent.ActivateSegment(s => s.GripType == EditorItemSegmentGripType.BottomRight, true);
            if (!isActivated)
                isActivated = this.CurrentInsertComponent.ActivateSegment(s => s.GripType == EditorItemSegmentGripType.MiddleRight, true);

            this.CurrentInsertComponent.MouseChange(args);
        }
        /// <summary>
        /// Vkládání komponenty, průběh Drag: probíhá resize
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentDragMove(EditInteractiveEventArgs args)
        {
            if (this.CurrentInsertComponent == null) return;
            this.CurrentInsertComponent.MouseChange(args);
        }
        /// <summary>
        /// Vkládání komponenty, konec Drag: dokončení vkládání komponenty
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentDragEnd(EditInteractiveEventArgs args)
        {
            this.CurrentInsertComponent.MouseChange(args);
            this.InsertComponentDone(args);
        }
        /// <summary>
        /// Akce při ukončení insertu jednoho každého objektu.
        /// Vyvolá aktivitu dokumentu, která zajistí zápis do Reverteru (možnost Undo po vložení objektu).
        /// Pokud není aktivní zámek IsLockedInsertObjectMode, pak ukončí režim Insert.
        /// </summary>
        /// <param name="args"></param>
        private void InsertComponentDone(EditInteractiveEventArgs args)
        {
            if (this.CurrentInsertComponent == null) return;

            // Komponentu umístíme na dané vizuální souřadnice, včetně jejího zařazení do Band
            // this.CurrentInsertComponent.SetVisualBound(this.CurrentInsertComponent.CurrentVisualArea);

            // Zavolám metodu DetectChangeOnMouseChange() aktuální komponenty, ale se stavem Leave, aby zhasla interaktivitu (segmenty)
            EditMouseChangeType change = args.MouseChange;
            args.MouseChange = EditMouseChangeType.Leave;
            this.CurrentInsertComponent.MouseChange(args);
            args.MouseChange = change;

            // Zajistím, že komponenta bude selectována (sama), a zobrazí se v property gridu:
            this.Document.SelectComponentOne(this.CurrentInsertComponent, false);

            // Pokud není zámek na režim insert, ukončím tento režim:
            if (!this.IsLockedInsertObjectMode)
            {
                this.CurrentObjectCommandKey = null;
                this.ObjectState = EditorObjectState.Select;       // Z režimu Insert přecházím do Select
                // Následující není potřeba, provede se v metodě this.Document.EditorInsertComponentDone():
                // this.Document.UIRefresh();
            }

            // Sdělím dokumentu, že jsme dokončili insert komponenty (dokument si sám provede aktualizaci reverteru):
            this.Document.EditorInsertComponentDone(this.CurrentInsertComponent);
            this.CurrentInsertComponent = null;

            // Provedu ReDraw vrstvy 1 (komponenty neinteraktivní), což způsobí i překreslení vrstvy 2 (interaktivní):
            args.ReDrawLayer(EditorLayer.Components);
            args.NeedCursor = SysCursorType.Default;
        }
        /// <summary>
        /// Zde je uložena komponenta, která se právě přidává do reportu.
        /// Vloží se sem při MouseDown v režimu this.IsInInsertObjectMode == true,
        /// následně se upravuje její velikost, a při MouseUp se sem vloží null a ukončí se režim IsInInsertObjectMode.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected ReportComponent CurrentInsertComponent { get; set; }
        #endregion
        #region Propojení s toolbarem a menu
        /// <summary>
        /// Metoda volaná z UI mimo this control: z Menu a z Toolbaru, požadavek na akci
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        internal void RunToolbarItemAction(object sender, EditorCommandExecuteArgs args)
        {
            this.RunCommand(new EditorCommandRunArgs(args.CommandKey, args.Data));
        }
        /// <summary>
        /// Určí, zda pro danou klávesu máme nějakou akci, a zajistí její provedení.
        /// Klávesový rozcestník.
        /// </summary>
        /// <param name="e"></param>
        internal void RunKeyboardAction(KeyEventArgs e)
        {
           // diagnostika kláves:
/*

    Time             Modifiers      KeyValue    KeyCode   
09:19:50.661         . Ctrl .     :      17  =  ControlKey
09:19:51.052         . Ctrl .     :      67  =  C
09:19:54.260         . Ctrl .     :      17  =  ControlKey
09:19:54.740         . Ctrl .     :      86  =  V
09:19:57.012         . Ctrl .     :      17  =  ControlKey
09:19:57.419         . Ctrl .     :      88  =  X
09:20:00.252         .      .     :      46  =  Delete
09:20:02.164         .      .     :      27  =  Escape
------------------------------------
09:21:51.777         .      .     :      67  =  C
09:21:53.033         . Ctrl .     :      17  =  ControlKey
09:21:53.289         . Ctrl .     :      67  =  C
09:21:56.793         .      .     :      86  =  V
09:21:57.633         . Ctrl .     :      17  =  ControlKey
09:21:57.921         . Ctrl .     :      86  =  V
09:22:01.347         .      . Alt :      18  =  Menu
09:22:01.529         .      . Alt :      86  =  V
09:22:04.761   Shift .      .     :      16  =  ShiftKey
09:22:04.961   Shift .      .     :      86  =  V
09:22:06.097         .      .     :      86  =  V
09:22:06.793         .      .     :      20  =  Capital
09:22:07.321         .      .     :      86  =  V
09:22:08.114         .      .     :      20  =  Capital
09:22:09.098         .      .     :      27  =  Escape


*/


            // Diagnostiku řeší Steward:
            string info = "EditorControl.Key:   " +
                (e.Shift ? "Shift" : "     ") + " . " +
                (e.Control ? "Ctrl" : "    ") + " . " +
                (e.Alt ? "Alt" : "   ") + " : " +
                "KeyValue=" + e.KeyValue.ToString().PadLeft(7) + "; " +
                "KeyCode=" + e.KeyCode.ToString();
            // Steward.Diagnostic.InfoAddLineWithTime(info);
            if (e.KeyValue == 27)
                // Steward.Diagnostic.InfoAddLineWithTime("----------------------------------------------------------------------------------");
            
            // Fyzickou aktivitu řeší Document: pokud pro danou klávesovou kombinaci existuje Command, provede jej nyní a vrátí true.
            //  To jest: v průběhu metody this.Document.KeyEventResolve() může být volána metoda this.RunCommand().
            if (this.Document.UIKeyEventResolve(e.Modifiers, e.KeyCode))
            {
                e.Handled = true;
                e.SuppressKeyPress = true;
            }
        }
        #endregion
        #region Provádění akcí vyvolaných z menu, z toolbaru a z klávesnice = RunCommand()
        /// <summary>
        /// Provedení dané akce.
        /// Poznámka: zdejší metodu volá v zásadě jen Document, takže akce s dokumentem sem nechodí = chodí sem jen ty commandy, které nevyřešil Document v sobě.
        /// Nemusím tedy ošetřovat commandy typu DocumentSave, Load, Print, atd.
        /// </summary>
        /// <param name="commandKey"></param>
        /// <returns></returns>
        internal void RunCommand(EditorCommandKey commandKey)
        {
            this.RunCommand(new EditorCommandRunArgs(commandKey, null));
        }
        /// <summary>
        /// Provedení dané akce.
        /// Poznámka: zdejší metodu volá v zásadě jen Document, takže akce s dokumentem sem nechodí = chodí sem jen ty commandy, které nevyřešil Document v sobě.
        /// Nemusím tedy ošetřovat commandy typu DocumentSave, Load, Print, atd.
        /// </summary>
        /// <param name="runArgs"></param>
        /// <returns></returns>
        internal void RunCommand(EditorCommandRunArgs runArgs)
        {
            EditorCommandResult result = this._RunCommandRun(runArgs);
            this._RunCommandResults(result);
        }
        /// <summary>
        /// Provede akce, které jsou specifikovány v odpovědi akce menu (EditorCommandResult)
        /// </summary>
        /// <param name="result"></param>
        private void _RunCommandResults(EditorCommandResult result)
        {
            if (result != null)
            {
                if (result.NeedLayout)
                    this.LayoutInnerDimensionsSet();
                if (result.NeedDraw)
                    this.ReDraw();
                if (result.NeedRefreshUI)
                    this.Document.UIRefresh();
            }
        }
        /// <summary>
        /// Provede příkaz editoru předaný v parametru, vrátí výsledky.
        /// </summary>
        /// <param name="runArgs"></param>
        /// <returns></returns>
        private EditorCommandResult _RunCommandRun(EditorCommandRunArgs runArgs)
        {
            switch (runArgs.CommandKey.CommandType)
            {
                case EditorCommandType.Zoom:
                    return this._RunCommandRunZoom(runArgs);
                case EditorCommandType.Object:
                    return this._RunCommandRunObject(runArgs);
                case EditorCommandType.ObjectInsert:
                    return this._RunCommandRunObjectInsert(runArgs);
            }
            return null;
        }
        /// <summary>
        /// Aktuální příkaz z hlediska práce s objekty
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public EditorCommandKey CurrentObjectCommandKey { get; protected set; }
        /// <summary>
        /// Aktuální stav editoru z hlediska práce s objekty Component a Band (Select - Anchor - Insert)
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public EditorObjectState ObjectState { get; protected set; }
        #region Změna měřítka
        /// <summary>
        /// Provede příkaz editoru typu Zoom, vrátí odpověď
        /// </summary>
        /// <param name="runArgs"></param>
        /// <returns></returns>
        private EditorCommandResult _RunCommandRunZoom(EditorCommandRunArgs runArgs)
        {
            ViewZoomMode viewZoomMode = _RunCommandRunZoomGetMode(runArgs.CommandKey);
            if (viewZoomMode != ViewZoomMode.None)
            {
                this.ViewPort.ZoomMode = viewZoomMode;             // ViewPort si přepočte Zoom i návazná měřítka
                return new EditorCommandResult() { NeedLayout = true, NeedDraw = true };
            }
            return null;
        }
        /// <summary>
        /// Vrátí ViewZoomMode na základě specifikace příkazu
        /// </summary>
        /// <param name="commandKey"></param>
        /// <returns></returns>
        private ViewZoomMode _RunCommandRunZoomGetMode(EditorCommandKey commandKey)
        {
            EditorCommandZoomType action;
            if (!commandKey.TryGetSpecification<EditorCommandZoomType>(out action))
                return ViewZoomMode.None;
            switch (action)
            {
                case EditorCommandZoomType.ZoomOriginal: return ViewZoomMode.OriginalSize;
                case EditorCommandZoomType.ZoomIn: return ViewZoomMode.ZoomInStep;
                case EditorCommandZoomType.ZoomOut: return ViewZoomMode.ZoomOutStep;
                case EditorCommandZoomType.ZoomBestFitWidth: return ViewZoomMode.WholeWidth;
                case EditorCommandZoomType.ZoomWholePage: return ViewZoomMode.WholePage;
            }
            return ViewZoomMode.None;
        }
        #endregion
        #region Práce s objekty: Select, Anchor, LockInsert
        /// <summary>
        /// Provede příkaz editoru typu Object, vrátí odpověď
        /// </summary>
        /// <param name="runArgs"></param>
        /// <returns></returns>
        private EditorCommandResult _RunCommandRunObject(EditorCommandRunArgs runArgs)
        {
            EditorCommandObjectType action;
            if (!runArgs.CommandKey.TryGetSpecification<EditorCommandObjectType>(out action))
                return null;

            switch (action)
            {
                case EditorCommandObjectType.ObjectSelect:
                    this.ObjectState = EditorObjectState.Select;
                    this.CurrentObjectCommandKey = runArgs.CommandKey;
                    return new EditorCommandResult() { NeedRefreshUI = true };
                case EditorCommandObjectType.ObjectAnchor:
                    this.ObjectState = EditorObjectState.Anchor;
                    this.CurrentObjectCommandKey = runArgs.CommandKey;
                    return new EditorCommandResult() { NeedRefreshUI = true };
                case EditorCommandObjectType.ObjectLockInsert:
                    this.IsLockedInsertObjectMode = !this.IsLockedInsertObjectMode;
                    return new EditorCommandResult() { NeedRefreshUI = true };
            }
            return null;
        }
        /// <summary>
        /// Příznak, zda režim InsertObjectMode je "zamčen".
        /// true: po dokončení insertování jednoho objektu se nepřejde do Select režimu, ale uživatel může ihned vkládat další nový objekt.
        /// false:  po dokončení insertování objektu se přejde zpátky do Select režimu (implicitní stav).
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsLockedInsertObjectMode { get; protected set; }
        #endregion
        #region Insertování objektu v editoru
        /// <summary>
        /// Provede příkaz editoru typu InsertObject, vrátí odpověď
        /// </summary>
        /// <param name="runArgs"></param>
        /// <returns></returns>
        private EditorCommandResult _RunCommandRunObjectInsert(EditorCommandRunArgs runArgs)
        {
            this.ObjectState = EditorObjectState.Insert;
            this.CurrentObjectCommandKey = runArgs.CommandKey;
            return new EditorCommandResult() { NeedRefreshUI = true };
        }
        /// <summary>
        /// Obsahuje true v případě, kdy do reportu budeme vkládat (nebo již vkládáme objekt). Pak je chování editoru jiné.
        /// Aktuální stav editoru reprezentuje property this.ObjectState.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsInInsertObjectMode
        {
            get
            {
                EditorCommandKey commandKey = this.CurrentObjectCommandKey;
                if (commandKey == null) return false;
                return (this.ObjectState == EditorObjectState.Insert && 
                        commandKey.CommandType == EditorCommandType.ObjectInsert &&
                        commandKey.CommandSpecification != null &&
                        commandKey.CommandSpecification is Type);
            }
        }
        #endregion
        #endregion
    }
    #region enum EditorObjectState : stav editoru vzhledem k objektům (Select, Anchor, Insert)
    /// <summary>
    /// EditorObjectState : stav editoru vzhledem k objektům (Select, Anchor, Insert)
    /// </summary>
    public enum EditorObjectState
    {
        /// <summary>
        /// Neurčený stav
        /// </summary>
        None = 0,
        /// <summary>
        /// Select = možnost výběru objektů myší, jejich standardní interaktivita (Move, Resize, Select)
        /// </summary>
        Select,
        /// <summary>
        /// Anchor = zobrazení ukotvení objektů, možnost změny (klikáním na zobrazené kotvy)
        /// </summary>
        Anchor,
        /// <summary>
        /// Insert = existující objekty nereagují, na pozici myši bude vkládán nový objekt
        /// </summary>
        Insert
    }
    #endregion
    #region class EditorCommandRunArgs : data pro provádění příkazu editoru
    /// <summary>
    /// EditorCommandRunArgs : data pro provádění příkazu editoru (příkaz + data)
    /// </summary>
    internal class EditorCommandRunArgs
    {
        internal EditorCommandRunArgs(EditorCommandKey commandKey, object data)
        {
            this.CommandKey = commandKey;
            this.Data = data;
        }
        /// <summary>
        /// Specifikace příkazu
        /// </summary>
        public EditorCommandKey CommandKey { get; private set; }
        /// <summary>
        /// Další data
        /// </summary>
        public object Data { get; private set; }
    }
    #endregion
    #region class EditorCommandResult : data, která se vrací po provedení příkazu v editoru (Command). Na základě tohoto výsledku reaguje editor (změna layoutu, draw, atd).
    internal class EditorCommandResult
    {
        /// <summary>
        /// V důsledku doběhnutí commandu je třeba provést rekalkulaci layoutu editoru
        /// </summary>
        public bool NeedLayout { get; set; }
        /// <summary>
        /// V důsledku doběhnutí commandu je třeba provést překreslení obsahuj editoru
        /// </summary>
        public bool NeedDraw { get; set; }
        /// <summary>
        /// V důsledku doběhnutí commandu je třeba provést refresh UI (menu, toolbar)
        /// </summary>
        public bool NeedRefreshUI { get; set; }
    }
    #endregion
    #region class EditorLayer, EditorPart : obsahují Int32 konstanty, které specifikují vrstvy (Layer) a části editoru (Part).
    /// <summary>
    /// Vrstvy v editoru
    /// </summary>
    public class EditorLayer
    {
        /// <summary>
        /// Podkladová vrstva: BandHead, BandRow, Rulers, Scrollbars.
        /// Uvedené komponenty jsou kresleny v této vrstvě i když jsou interaktivně měněny, protože jejich změna se promítá do všech vyšších vrstev okamžitě.
        /// </summary>
        public const int Ground = 0;
        /// <summary>
        /// Vrstva komponent, pokud nejsou interaktivně měněny. Změna daná MouseOver se promítá do této vrstvy.
        /// </summary>
        public const int Components = 1;
        /// <summary>
        /// Vrstva komponent které jsou interaktivně měněny.
        /// Na začátku změny musí být vykreslena jak původní vrstva (Components), tak interaktivní vrstva (Interactive), protože z původní vrstvy se komponenty odmazávají.
        /// Na konci editace rovněž tak (komponenta přechází z vrstvy Interactive do Components). Nemusí se ale kreslit vrstva Ground.
        /// V průběhu editace se kreslí pouze vrstva Interactive.
        /// </summary>
        public const int Interactive = 2;
    }
    /// <summary>
    /// Oblasti v prostoru editoru (dokument, scrollbary, pravítko, Bands, splittery...).
    /// Pod touto hodnotou jsou ukládany do ViewPortu vizuální souřadnice těchto prostor.
    /// </summary>
    public class EditorPart
    {
        /// <summary>Nevyužitý prostor, nemá záznam ve ViewPortu</summary>
        public const int NotUsed = 0;
        /// <summary>Celý vnitřní prostor Controlu</summary>
        public const int WholeClientArea = NotUsed + 1;
        /// <summary>Prostor, do něhož se kreslí dokument</summary>
        public const int Document = WholeClientArea + 1;
        /// <summary>Prostor obsazený záhlavím svazků (Bands), vlevo od dokumentu</summary>
        public const int BandHeader = Document + 1;
        /// <summary>Prostor záhlaví reportu, vlevo nahoře</summary>
        public const int ReportHeader = BandHeader + 1;
        /// <summary>Pravítko, nahoře nad dokumentem</summary>
        public const int HorizontalRule = ReportHeader + 1;
        /// <summary>Oddělovač Bands a dokumentu</summary>
        public const int BandDocumentSplitter = HorizontalRule + 1;
        /// <summary>Oddělovač pravítka a dokumentu</summary>
        public const int RuleDocumentSplitter = BandDocumentSplitter + 1;
        /// <summary>Svislý scrollbar vpravo od dokumentu</summary>
        public const int VerticalScrollBar = RuleDocumentSplitter + 1;
        /// <summary>Vodorovný scrollbar dole pod dokumentem</summary>
        public const int HorizontalScrollBar = VerticalScrollBar + 1;
    }
    #endregion
}
